package com.yuqian.JavaSe.java.sinterface;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

//Cloneable接口注释
/**
 * 学习java Cloneable接口
 * @author yuqian
 *
 *以下是Cloneable接口的解释（JDK源码）
 *
 *此类实现了 Cloneable 接口，以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。 如果在没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法，则会导致抛出 CloneNotSupportedException异常。 
 *按照惯例，实现此接口的类应该使用公共方法重写 Object.clone（它是受保护的）。请参阅 Object.clone()，以获得有关重写此方法的详细信息。 
 *注意，此接口不包含 clone 方法。因此，因为某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的，也无法保证它将获得成功。
 *解读：Cloneable接口没有任何方法，仅是个标志接口(tagging interface)，若要具有克隆能力，实现Cloneable接口的类必须重写从Object继承来的clone方法，并调用Object的clone方法（见下面Object#clone的定义）
 *
 *
 */
//Object  protected native Object clone() throws CloneNotSupportedException; 方法的注释
/**
 * 创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。一般来说，对于任何对象 x，如果表达式： 
 *x.clone() != x是正确的，则表达式： x.clone().getClass() == x.getClass()将为 true，但这些不是绝对条件。
 *一般情况下是： x.clone().equals(x)将为 true，但这不是绝对条件。 
 *按照惯例，返回的对象应该通过调用 super.clone  获得。如果一个类及其所有的超类（Object 除外）都遵守此约定，则 x.clone().getClass() == x.getClass()。 
 *按照惯例，此方法返回的对象应该独立于该对象（正被克隆的对象）。要获得此独立性，在 super.clone 返回对象之前，有必要对该对象的一个或多个字段进行修改。这通常意味着要复制包含正在被克隆对象的内部“深层结构”的所有可变对象，并使用对副本的引用替换对这些对象的引用。如果一个类只包含基本字段或对不变对象的引用，那么通常不需要修改 super.clone 返回的对象中的字段。 
 *Object 类的 clone 方法执行特定的克隆操作。首先，如果此对象的类不能实现接口 Cloneable，则会抛出 CloneNotSupportedException。注意：所有的数组都被视为实现接口 Cloneable。否则，此方法会创建此对象的类的一个新实例，并像通过分配那样，严格使用此对象相应字段的内容初始化该对象的所有字段；这些字段的内容没有被自我克隆。所以，此方法执行的是该对象的“浅表复制”，而不“深层复制”操作。 
 *Object 类本身不实现接口 Cloneable，所以在类为 Object 的对象上调用 clone 方法将会导致在运行时抛出异常 。
 * @author yuqian
 *
 */
//为什么要使用clone
/**
 * 
 * 一个对象内部有可变对象实例的话，public API不应该直接返回该对象的引用，以防调用方的code改变该对象的内部状态。这个时候可以返回该对象的clone
 * @author yuqian
 *
 */

//浅克隆和深
/**
 * ①浅克隆――若要克隆某个对象，只克隆他自身以及他包含的所有对象的引用地址。
 * ②深克隆――克隆除自身以外所有的对象，包括自身所包含的所有对象实例。由具体的需求决定深克隆的层次(N层克隆)。
 */


//java native 实现的是浅克隆，原因：
/**
 *1.简单高效:java实现克隆的过程是先开辟一个和目标对象一样的内存空间，然后将目标对象内存复制到先开辟的内存
 *（native方法比普通java方法要快,而且如果使用java方法要遍历目标对象，然后赋值）
 *2. 不给目标对象关联的类强加意义（如果是深克隆，就必须要求目标对象关联的类必须也实现Cloneable接口）
 *3.有可能破坏关联类语义(如果是深克隆，而关联对象是单例的，这样就破坏了单例)
 *4.更具灵活性(如果目标对象关联的是一个不可变对象,就不需要深克隆,程序员可以自己控制)
 */
public class SCloneable implements Cloneable{
	
	
	public static void main(String[] args) {
		System.out.println("浅克隆:");
		ShallowClone sc = new ShallowClone();
		ShallowClone scCl = sc.clone();
		System.out.println(sc.sCloneable==scCl.sCloneable);
		
		System.out.println("------------------------");
		System.out.println("深克隆:");
		DeepClone dc = new DeepClone();
		DeepClone dcCl = dc.clone();
		System.out.println(dc.sCloneable==dcCl.sCloneable);
		
		
	}
	
	
	
	
	@Override
	public SCloneable clone(){
		Object ob =null;
		try {
			ob= super.clone();
		} catch (CloneNotSupportedException e) {
			//若该类没有实现Cloneable 则会抛出CloneNotSupportedException异常
			e.printStackTrace();
		}
		return (SCloneable)ob;
	}
	
	/**
	 * 序列化实现对象克隆
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public SCloneable copy() throws IOException, ClassNotFoundException{
		//this.getClass().getResourceAsStream("");
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(bos);
		oos.writeObject(this);
		
		ByteArrayInputStream bis =new ByteArrayInputStream(bos.toByteArray());
		ObjectInputStream ois = new ObjectInputStream(bis);
		
		return (SCloneable)ois.readObject();
	}
}

//浅克隆
class ShallowClone implements Cloneable{
	
	SCloneable sCloneable = new SCloneable();
	
	@Override
	public ShallowClone clone(){
		Object ob =null;
		try {
			ob= super.clone();
		} catch (CloneNotSupportedException e) {
			//若该类没有实现Cloneable 则会抛出CloneNotSupportedException异常
			e.printStackTrace();
		}
		return (ShallowClone)ob;
	}
}

//深克隆 
class DeepClone implements Cloneable{
SCloneable sCloneable = new SCloneable();
	
	@Override
	public DeepClone clone(){
		Object ob =null;
		try {
			ob= super.clone();
		} catch (CloneNotSupportedException e) {
			//若该类没有实现Cloneable 则会抛出CloneNotSupportedException异常
			e.printStackTrace();
		}
		((DeepClone)ob).sCloneable =this.sCloneable.clone();
		return (DeepClone)ob;
	}
}
